package battleOfTheSevenSeas.Client;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.border.Border;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

public class TheGreatSea {

	private int windowWidth = 950;
	private int windowHeight = 600;
	private int gridSize = 17;
	private Client clt;
	private ArrayList<String> indexGrille = new ArrayList<String>(
			Arrays.asList("","A","B","C","D","E","F","G","H",
					"I","J","K","L","M","N","O","P"));
	private Color[] playerColors = {
			Color.YELLOW, Color.PINK, 
			Color.GREEN, Color.CYAN};


	// Components
	private JFrame mainFrame = new JFrame();
	private JPanel aireDeClavardage = new JPanel();
	private JTextPane truePlayer = new JTextPane();
	private JTextPane trueSystem = new JTextPane();
	private JTextPane aireDeClavardageSys = new JTextPane();
	private JTextPane aireDeClavardagePlay = new JTextPane();
	private JMenuBar mainMenuBar = new JMenuBar();
	private JPanel barAreaPane = new JPanel();
	private JTextPane trueLegend = new JTextPane();
	private JPanel brutalLegend = new JPanel();
	private JPanel iAmLegend = new JPanel();
	private JPanel oceanAreaPane = new JPanel();
	private JPanel clavardageAreaPane = new JPanel();
	private JPanel mainAreaPane = new JPanel();
	private JTextField jfield = new JTextField();
	private JButton connect = new JButton("Play");
	private JButton login = new JButton("Login");
	private JButton register = new JButton("Register");
	private JButton spectate = new JButton("Spectate");
	private JButton sconnect = new JButton("Server");

	// Borders and colors
	private Color gridBorder = new Color(0, 51, 51);
	private Color oceanBlue = new Color(0, 0, 51);
	private Color gridBlue = new Color(255,255,155);
	private Color tirOuch = Color.GRAY;
	private Color tirMiss = Color.BLACK;
	private Color tirTouche = Color.RED;
	private Border thinLine = BorderFactory.createLineBorder(gridBorder, 1);
	private Border borderLine = BorderFactory.createLineBorder(gridBorder, 2);
	private Color[] tabLegende = {oceanBlue, tirOuch, tirMiss, tirTouche};

	public TheGreatSea() {

		// Fenetre principale
		resetTitle();
		mainFrame.setLocation(150,100);
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.addWindowStateListener(new MyWindowListener());
		mainFrame.setSize(windowWidth, windowHeight);
		mainFrame.setResizable(false);

		// Barre de menu
		barAreaPane.setLayout(new BoxLayout(barAreaPane, BoxLayout.Y_AXIS));

		addMyMenuListeners(new MyMenuListener());
		mainMenuBar.add(connect);
		mainMenuBar.add(login);
		mainMenuBar.add(register);
		mainMenuBar.add(spectate);
		mainMenuBar.add(sconnect);

		// Cote Grille
		oceanAreaPane.setLayout(new GridLayout(gridSize, gridSize));
		oceanAreaPane.setPreferredSize(
				new Dimension(windowHeight,windowHeight));	

		for (int i = 0; i < (gridSize * gridSize); i++){
			JTextPane gp = new JTextPane();
			gp.setBorder(borderLine);
			gp.setEditable(false);
			oceanAreaPane.add(gp, i);
			String panelText = "";
			Color panelColor = oceanBlue;
			setStyleCenter(gp);
			if (i > ((gridSize * gridSize) - gridSize) && (i % gridSize) != 0){
				// Cases contenant les abscisses (lettres A à P)
				panelText = (String.valueOf((i % gridSize) - 1));
				panelColor = gridBlue;

			} else if (i % gridSize == 0){
				// Cases contenant les ordonnées (nombres 1 à 16)
				panelText = getLetter(gridSize - (i/gridSize) - 1);
				panelColor = gridBlue;
			}
			gp.setText(panelText);
			gp.setBackground(panelColor);
		}
		// Cote Texte
		setStyleCenter(trueSystem);
		trueSystem.setBorder(thinLine);
		trueSystem.setEditable(false);
		trueSystem.setText("Messages Système");
		trueSystem.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 25));		
		aireDeClavardageSys.setEditable(false);
		aireDeClavardageSys.setBorder(thinLine);
		JScrollPane editorScrollPane = new JScrollPane(aireDeClavardageSys);
		editorScrollPane.setWheelScrollingEnabled(true);
		editorScrollPane.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 200));		
		int vsa = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
		editorScrollPane.setVerticalScrollBarPolicy(vsa);

		setStyleCenter(truePlayer);
		truePlayer.setBorder(thinLine);
		truePlayer.setEditable(false);
		truePlayer.setText("Messages Joueurs");
		truePlayer.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 25));		
		aireDeClavardagePlay.setEditable(false);
		aireDeClavardagePlay.setBorder(thinLine);
		JScrollPane editorScrollPane2 = new JScrollPane(aireDeClavardagePlay);
		editorScrollPane2.setWheelScrollingEnabled(true);
		editorScrollPane2.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 200));		
		int vsa2 = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
		editorScrollPane2.setVerticalScrollBarPolicy(vsa2);

		aireDeClavardage.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 480));		
		aireDeClavardage.setLayout(
				new BoxLayout(aireDeClavardage, BoxLayout.Y_AXIS));
		aireDeClavardage.add(trueSystem);
		aireDeClavardage.add(editorScrollPane);
		aireDeClavardage.add(truePlayer);
		aireDeClavardage.add(editorScrollPane2);

		// Zone de legende
		brutalLegend.setLayout(new BoxLayout(brutalLegend, BoxLayout.Y_AXIS));
		setStyleCenter(trueLegend);
		trueLegend.setEditable(false);
		trueLegend.setBorder(thinLine);
		trueLegend.setText("Légende");
		trueLegend.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 25));		
		iAmLegend.setLayout(new GridLayout(4, 4));
		iAmLegend.setPreferredSize(
				new Dimension(windowWidth - windowHeight, 120));
		for (int j = 0; j < 16; j++){
			JTextPane gp = new JTextPane();
			gp.setBorder(thinLine);
			gp.setEditable(false);
			iAmLegend.add(gp);
			setStyleCenter(gp);
			if (j % 4 == 0){
				gp.setText("Joueur " + ((j / 4) + 1));
				gp.setBackground(playerColors[j / 4]);
			}
			if (j % 4 == 2){
				gp.setBackground(tabLegende[j / 4]);
			}
			if (j % 4 == 3){
				gp.setText(getTypeCase(tabLegende[j / 4]));
			}
		}
		brutalLegend.add(trueLegend);
		brutalLegend.add(iAmLegend);

		KeyAdapter pressEnter = new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				int key = e.getKeyCode();
				if (key == KeyEvent.VK_ENTER) {
					Toolkit.getDefaultToolkit().beep();
					if (e.getSource() instanceof JTextField){
						JTextField tf = (JTextField) e.getComponent();
						//						String s = "TALK/" + tf.getText() + "/";
						if (clt.isConnected()){
							String s = tf.getText();
							clt.writeToServer(s);
							tf.setText("");
						}
					}
				}
			}
		};
		jfield.addKeyListener(pressEnter);
		clavardageAreaPane.setLayout(
				new BoxLayout(clavardageAreaPane, BoxLayout.Y_AXIS));
		clavardageAreaPane.add(aireDeClavardage);
		clavardageAreaPane.add(jfield);
		barAreaPane.add(mainMenuBar);
		barAreaPane.add(brutalLegend);
		barAreaPane.add(clavardageAreaPane);

		mainAreaPane.setLayout(new BoxLayout(mainAreaPane, BoxLayout.X_AXIS));
		mainAreaPane.add(oceanAreaPane);
		mainAreaPane.add(barAreaPane);
		mainFrame.add(mainAreaPane);

		mainFrame.setVisible(true);

	}

	/**
	 * Place le sous-marin de coordonnées s
	 * du joueur player sur la grille
	 * @param s
	 * @param player
	 */
	public void placeShip(String s, int player){
		String[] casesAModif = s.split("/");
		for (int i = 0; i <= casesAModif.length - 2; i = i + 2){
			changePanelColor(casesAModif[i], 
					casesAModif[i + 1], 
					getPlayerColor(player));
		}
	}

	/**
	 * Séquence de placement de sous marin
	 * Ouvre une boite pour demander si l'utilisateur veux un placement
	 * vertical ou horizontal puis demande les coordonnées du coin supérieur
	 * gauche du sous-marin à placer
	 * @param taille
	 */
	public void putshipBox(int taille){
		Object[] options = {"Horizontal","Vertical"};
		Object[] abscisse = {"A","B","C","D","E","F","G","H",
				"I","J","K","L","M","N","O","P"};
		Object[] ordonnee = {"0","1","2","3","4","5","6","7","8",
				"9","10","11","12","13","14","15"};
		Object[] reducedAbsTab = null;
		Object[] reducedOrdTab = null;
		String boxTitle = "Placement sous-marin de taille " + taille;				 

		String q = "Veuillez sélectionner le sens du sous-marin.";
		int n = createExtOptionBox(q,	
				boxTitle, options);
		if (n == JOptionPane.NO_OPTION){
			// Placement horizontal
			reducedAbsTab = reduceTabSize(abscisse, taille);
			reducedOrdTab = ordonnee.clone();
		} else if (n == JOptionPane.YES_OPTION){
			// Placement vertical
			reducedOrdTab = reduceTabSize(ordonnee, taille);
			reducedAbsTab = abscisse.clone();
		}
		String ord = createExtInputBox("Veuillez sélectionner l'ordonnée",
				boxTitle, reducedOrdTab); 
		String abs = createExtInputBox("Veuillez sélectionner l'abscisse",
				boxTitle, reducedAbsTab);

		// Cas où l'utilisateur a fermé la fenêtre de choix
		if (n == JOptionPane.CLOSED_OPTION || abs == null || ord == null){
			// Si l'on saute une des étapes, on renverra un PUTSHIP erron� 
			// pour recevoir WRONG
			clt.writeToServer("PUTSHIP/");
		} else {
			String position = ord + "/" + abs + "/";

			int i = 1;
			while (i < taille){
				if (n == JOptionPane.NO_OPTION){
					position += ord  
							+ "/" 
							+ getLetter(getInt(abs) + i) 
							+ "/";
				}else{
					position += (Integer.valueOf(ord) + i)  
							+ "/" 
							+ getLetter(getInt(abs))
							+ "/";
				}
				i++;
			}
			clt.setPlacementSousMarin(position);
			clt.writeToServer("PUTSHIP/" + position);
		}
	}

	/**
	 * Renvoi la liste des actions possibles selon la position du drone
	 * @param ord
	 * @param abs
	 * @return Object[]
	 */
	public Object[] getDroneTab(int ord, int abs){
		ArrayList<String> actions = 
				new ArrayList<String>(Arrays.asList("U","D","L","R","E"));

		if (ord < 1){
			actions.remove("L");
		} else if (ord > 14){
			actions.remove("R");
		}

		if (abs < 2){
			actions.remove("D");
		} else if (abs > 15){
			actions.remove("U");
		}
		return actions.toArray();
	}

	/**
	 * Reduit le tableau src à la taille src.length - (t-1)
	 * @param src
	 * @param t
	 * @return src[src.length - (t-1)] 
	 */
	public Object[] reduceTabSize(Object[] src, int t){
		int sizet = src.length - (t - 1);
		Object[] res = new Object[sizet];
		for (int j = 0; j < sizet; j++){
			res[j] = src[j];
		}
		return res;
	}

	/**
	 * Renvoi la couleur associé à l'action color 
	 * @param color - String
	 * @return Color
	 */
	public Color getActionColor(String color){
		Color res = null;
		switch (color){
		case "touche" : 
			res = tirTouche;
			break;
		case "ouch" :
			res = tirOuch;
			break;
		case "miss" : 
			res = tirMiss;
			break;
		}
		return res;
	}

	/**
	 * Convertit l'entier i en chaine de caractère
	 * @param i
	 * @return String 
	 */
	public String getLetter(int i){
		return indexGrille.get(i);
	}

	/**
	 * Convertit la chaine s en int
	 * @param s
	 * @return int
	 */
	public int getInt(String s){
		return indexGrille.indexOf(s);
	}

	/**
	 * Renvoi la couleur du joueur selon son index
	 * @param i
	 * @return Couleur du joueur
	 */
	public Color getPlayerColor(int i){
		return playerColors[i];
	}

	/**
	 * Renvoi la couleur de la bordure de grille
	 * @return la couleur de la bordure de grille par defaut
	 */
	public Color getGridColor(){
		return gridBorder;
	}

	/**
	 * Rend le type de la case selon la couleur rentrée en paramètre
	 * @param c
	 * @return "Ocean", "Tir raté", "Ouch !" ou "Tir Touché"
	 */
	public String getTypeCase(Color c){
		String res = "";
		if (c.equals(oceanBlue)){
			res = "Ocean";
		} else if (c.equals(tirMiss)){
			res = "Tir Raté";
		} else if (c.equals(tirOuch)){
			res = "Ouch !";
		} else if (c.equals(tirTouche)){
			res = "Tir Touché";
		}
		return res;
	}

	/**
	 * Setter pour faire connaître le client
	 * Les dépendances structurelles croisées, c'est moche, mais bon !
	 * @param c
	 */
	public void setClient (Client c){
		this.clt = c;
	}

	/**
	 * Méthode lancée quand c'est notre tour de jouer 
	 * Actions du drone
	 * @param ord
	 * @param abs
	 * @param n
	 * @param player
	 */
	public void droneActions(String ord, String abs, String n, int player){
		String res = "ACTION/";
		int nbActions = Integer.valueOf(n);
		int ordonnee = Integer.valueOf(ord);
		int abscisse = getInt(abs);
		String boxtitle;
		String question = "Veuillez sélectionner une action.\n";
		while (nbActions > 0){
			boxtitle = "Joueur : " + clt.getJoueur().getPlayerName() 
					+ " | Actions : " + nbActions;
			Object[] actionTab = getDroneTab(ordonnee, abscisse);
			String ext = "Votre drone est en : " 
					+ getLetter(abscisse) + ordonnee;
			String action = null;
			while (action == null){
				action = createExtInputBox(question + ext, boxtitle, actionTab);
			}
			res += (action + "/");
			changeGridBorderColor(abscisse, ordonnee, gridBorder);
			switch (action){
			case "U" :
				abscisse++;
				break;
			case "D" :
				abscisse--;
				break;
			case "L" :
				ordonnee--;
				break;
			case "R" :
				ordonnee++;
				break;
			}
			changeGridBorderColor(abscisse, ordonnee, getPlayerColor(player));
			nbActions--;
		}
		clt.writeToServer(res);
		clt.updateDroneCoords(abscisse, ordonnee, player);
	}

	/**
	 * Met à jour le texte dans la fenetre de chat en rajoutant la ligne newT
	 * @param newT
	 */
	public synchronized void refreshText(String newT){
		String oldT = this.aireDeClavardageSys.getText();
		this.aireDeClavardageSys.setText(newT + "\n" + oldT);
	}

	public synchronized void refreshChat(String newT){
		String oldT = this.aireDeClavardagePlay.getText();
		this.aireDeClavardagePlay.setText(newT + "\n" + oldT);
	}

	/**
	 * Change la couleur de fond de la case
	 * @param ord - String
	 * @param abs - String
	 * @param color - Color
	 */
	public void changePanelColor(String ord, String abs, Color color){
		int indAbs = getInt(abs);
		int indOrd = Integer.valueOf(ord);
		changeGridBackgroundColor(indAbs, indOrd, color);
	}

	/**
	 * Change la couleur de la bordure de la case
	 * @param ord - String 
	 * @param abs - String 
	 * @param color - Color
	 */
	public void changePanelBorder(String ord, String abs, Color color){
		int indAbs = getInt(abs);
		int indOrd = Integer.valueOf(ord);
		changeGridBorderColor(indAbs, indOrd, color);
	}

	/**
	 * Change la couleur de fond de la case
	 * @param abs - int 
	 * @param ord - int
	 * @param c - Color
	 */
	public void changeGridBackgroundColor(int abs, int ord, Color c){
		int ind = ord + 1 + ((gridSize - 1 - abs) * gridSize);
		oceanAreaPane.getComponent(ind).setBackground(c);
	}

	/**
	 * Change la couleur de la bordure de la case
	 * @param abs
	 * @param ord
	 * @param c
	 */
	public void changeGridBorderColor(int abs, int ord, Color c){
		int ind = ord + 1 + ((gridSize - 1 - abs) * gridSize);
		JTextPane tpane = 
				(JTextPane) (oceanAreaPane.getComponent(ind));
		Border b = tpane.getBorder(); 
		Border b1 = BorderFactory.createLineBorder(c,2);
		if (b.equals(borderLine)){
			tpane.setBorder(b1);
		} else {
			tpane.setBorder(borderLine);
		}
	}

	/** 
	 * Crée une option box mais avec comme options de saisie les
	 * éléments du tableau d'objet o
	 * @param q
	 * @param t
	 * @param o
	 * @return YES_OPTION, NO_OPTION, CLOSED_OPTION
	 */
	public int createExtOptionBox(String q, String t, Object[] o){
		return JOptionPane.showOptionDialog(mainFrame, q, t,
				JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
				null, o, o[0]);
	}

	/** 
	 * Crée une input box mais avec comme options de saisie les
	 * éléments du tableau d'objet o
	 * @param q
	 * @param t
	 * @param o
	 * @return la valeur validée par l'utilisateur
	 */
	public String createExtInputBox(String q, String t, Object[] o){
		return (String) JOptionPane.showInputDialog(
				mainFrame, q, t,	JOptionPane.PLAIN_MESSAGE,
				null, o, o[0]);
	}

	/**
	 * Crée une input box simple
	 * @param question
	 * @param title
	 * @return
	 */
	public String createInputBox(String question, String title){
		return JOptionPane.showInputDialog(mainFrame, 
				question, title, JOptionPane.QUESTION_MESSAGE);
	}

	/**
	 * Permet de centrer le texte dans la JTextPane tp
	 * @param tp
	 */
	private void setStyleCenter(JTextPane tp){
		StyledDocument doc = tp.getStyledDocument();		
		MutableAttributeSet center = new SimpleAttributeSet();		
		StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
		doc.setParagraphAttributes(0, 0, center, true);

	}

	/**
	 * Affiche dans la légende les noms des différents joueurs
	 * à côté de leur couleur respective
	 * @param tabString
	 */
	public void updatePlayerNames(String[] tabString){
		for (int i = 1; i < tabString.length; i++){
			String t = tabString[i];
			JTextPane tp = 
					((JTextPane) iAmLegend.getComponent((4 * (i - 1)) + 1));
			setStyleCenter(tp);
			tp.setText(t);
		}
	}

	/**
	 * On rajoute le listener a tout les boutons du menu
	 * @param ml
	 */
	public void addMyMenuListeners(MyMenuListener ml){
		login.addActionListener(ml);
		connect.addActionListener(ml);
		register.addActionListener(ml);
		spectate.addActionListener(ml);
		sconnect.addActionListener(ml);
	}

	public class MyWindowListener implements WindowStateListener{

		@Override
		public void windowStateChanged(WindowEvent e) {
			// TODO Auto-generated method stub
			clt.closeClient();
		}

	}

	/**
	 * Listener pour les boutons du menu
	 * @author Thierry Wong
	 */
	public class MyMenuListener implements ActionListener{

		@Override
		public void actionPerformed(ActionEvent e) {
			String buttonName = ((JButton) e.getSource()).getText();
			String nameQ = "Veuillez saisir votre nom de joueur";
			String mdpQ = "Veuillez saisir votre mot de passe";
			String nomJoueur;
			String mdpJoueur;
			String title;
			String request = null;
			if (buttonName.compareTo("Play") == 0){
				if ((nomJoueur = createInputBox(nameQ, "Jouer")) != null){
					request = "CONNECT/" + nomJoueur + "/";					
				}
			} else if (buttonName.compareTo("Register") == 0){
				title = "Enregistrement";
				if ((nomJoueur = createInputBox(nameQ, title)) != null){
					if ((mdpJoueur = createInputBox(mdpQ, title)) != null){
						request = "REGISTER/" + nomJoueur + "/" + mdpJoueur + "/";
					}
				}
			} else if (buttonName.compareTo("Spectate") == 0){
				request = "SPECTATOR/";
			} else if (buttonName.compareTo("Login") == 0){
				title = "Connection";
				if ((nomJoueur = createInputBox(nameQ, title)) != null){
					if ((mdpJoueur = createInputBox(mdpQ, title)) != null){
						request = "LOGIN/" + nomJoueur + "/" + mdpJoueur + "/";
					}
				}
			}
			if (request != null){
				clt.writeToServer(request) ;
			}
			if (buttonName.compareTo("Server") == 0){
				title = "Server select";
				String question = "Veuillez saisir le nom de l'hôte :";
				String nomHote;
				if ((nomHote = createInputBox(question, title)) != null){
					clt.setHostName(nomHote);
					clt.setConnectedToServer(true);
				}
			}

		}
	}

	public void changeTitle(String s){
		mainFrame.setTitle(mainFrame.getTitle() + s);
	}

	public void resetTitle(){
		mainFrame.setTitle("[ Battle of the Seven Seas ]");
	}

	public void changeServerButtonState(boolean b){
		sconnect.setEnabled(b);
	}

	public void changeMenuState(boolean b){
		connect.setEnabled(b);
		login.setEnabled(b);
		register.setEnabled(b);
		spectate.setEnabled(b);
	}

	public void resetClient(){
		aireDeClavardagePlay.setText("");
		aireDeClavardageSys.setText("");
		resetGrid();
	}
	
	public void resetGrid(){
		for (Component caseGrille : oceanAreaPane.getComponents()){
			JTextPane tp = (JTextPane) caseGrille;
			if (!tp.getBackground().equals(gridBlue)){
				tp.setBackground(oceanBlue);
			}
			tp.setBorder(borderLine);
		}
	}
}
